package gw.com.android.presenter;

import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import gw.com.android.app.AppMain;
import gw.com.android.app.GTConfig;
import gw.com.android.presenter.http.Https;
import gw.com.android.utils.IOUtils;
import www.com.library.app.Logger;
import www.com.library.dialog.ToastPopWindow;

/**
 * @author jett
 * @since 2017-12-08.
 */
public class FMPresenter {

    private static final FMPresenter instance = new FMPresenter();
    private Https https;
    private Handler mHandler;
    private Runnable task;

    public static FMPresenter getInstance() {
        return instance;
    }

    private final String TAG = "FMPresenter";
    private MediaPlayer player;
    private final File dir;
    private final String datFilename = "mata.dat";
    private final String videoFilename = "hxfm.mp3";
    private String mUrl;
    private int breakPosition = 0;
    private int articleId = -1;
    private boolean articleIsNew = false;
    private boolean prepared = false;
    private boolean downloading = false;
    private List<FMListener> listeners;

    private FMPresenter() {
        dir = new File(AppMain.getApp().getExternalCacheDir() + "/hxfm/");
        if (!dir.exists()) {
            boolean mkdirs = dir.mkdirs();
            if (!mkdirs) {
                Logger.e(TAG, "make fm cache dir failed");
            }
        }
    }

    private void createPlayer() {
        if (player != null) {
            player.release();
            player = null;
        }
        player = new MediaPlayer();
        player.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                Logger.e(TAG, "[OnErrorListener] prepared error");
                release();
                new ToastPopWindow(AppMain.getApp(), "音频加载失败").show();
                return false;
            }
        });
        player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                completed();
            }
        });
        player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                prepared();
            }
        });
    }

    public synchronized void load(String url) {
        if (TextUtils.isEmpty(url)) {
            return;
        }
        if (url.equals(mUrl)) {
            Logger.d(TAG, "[load] an url has been loaded");
            if (player == null) {
                Logger.d(TAG, "[load] re prepare player");
                preparePlayer();
            }
            return;
        }
        if (!TextUtils.isEmpty(mUrl)) {
            reset();
        }
        mUrl = url;
        preparePlayer();
    }

    public void preparePlayer() {
        Logger.d(TAG, "[preparePlayer] prepare");
        File file = fetchLocal(mUrl);
        if (file == null || !file.exists()) {
            Logger.d(TAG, "[preparePlayer] init from net and cache this audio");
            initPlayer(mUrl);
            download(mUrl);
            return;
        }
        initPlayer(file.getPath());
    }

    public void release() {
        Logger.d(TAG, "[release] release player");
        prepared = false;
        MediaPlayer mp = this.player;
        this.player = null;
        if (mp != null) {
            breakPosition = mp.getCurrentPosition();
            mp.release();
        }
    }

    private void initPlayer(String path) {
        createPlayer();
        player.reset();
        try {
            player.setDataSource(path);
            player.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        if (!prepared || player == null) {
            return;
        }
        if (!player.isPlaying()) {
            player.start();
            breakPosition = 0;
            startTimmer();
            notifyStart();
        }
    }

    public void pause() {
        if (!prepared || player == null) {
            return;
        }
        if (player.isPlaying()) {
            player.pause();
            pauseTimmer();
            notifyPause();
        }
    }

    public void seekTo(int progress) {
        if (!prepared || player == null) {
            return;
        }
        player.seekTo(progress);
    }

    private void prepared() {
        Logger.d(TAG, "[prepared] prepared ready");
        prepared = true;
        if (breakPosition > 0) {
            player.seekTo(breakPosition);
        }
        notifyPrepared();
    }

    private void completed() {
        prepared = false;
        breakPosition = 0;
        if (player != null) {
            player.release();
            player = null;
        }
        pauseTimmer();
        notifyCompleted();
    }

    private void startTimmer() {
        if (mHandler == null) {
            mHandler = new Handler(Looper.getMainLooper());
        }
        task = new Runnable() {
            @Override
            public void run() {
                notifyProgress();
                mHandler.postDelayed(this, 500);
            }
        };
        mHandler.postDelayed(task, 500);
    }

    private void pauseTimmer() {
        if (mHandler != null && task != null) {
            mHandler.removeCallbacks(task);
        }
        mHandler = null;
        task = null;
    }

    private void reset() {
        Logger.e(TAG, "[reset] cancel download task");
        downloading = false;
        if (https != null) {
            https.cancel();
        }
        breakPosition = 0;
        release();
        notifyReset();
    }

    public boolean isPlaying() {
        return prepared && player != null && player.isPlaying();
    }

    public int getDuration() {
        if (!prepared || player == null) {
            return 0;
        }
        return player.getDuration();
    }

    public int getCurrentPosition() {
        if (!prepared || player == null) {
            return 0;
        }
        return player.getCurrentPosition();
    }

    private File fetchLocal(String url) {
        File file = new File(dir, datFilename);
        if (!file.exists()) {
            return null;
        }
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            String line = reader.readLine();
            if (TextUtils.isEmpty(line) || !line.equals(url)) {
                return null;
            }
            Logger.d(TAG, "[fetchLocal] acquire local succussfull");
            return new File(dir, videoFilename);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeSilent(reader);
        }
        return null;
    }

    private void saveLocal(String url) {
        File file = new File(dir, datFilename);
        if (file.exists()) {
            boolean delete = file.delete();
            if (!delete) {
                Logger.e(TAG, "");
            }
        }
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(file);
            writer.write(url);
            writer.close();
        } catch (FileNotFoundException e) {
            Logger.e(TAG, "[saveLocal] an error occured when save local..");
        } finally {
            IOUtils.closeSilent(writer);
        }
    }

    private void download(final String url) {
        if (downloading) {
            return;
        }
        downloading = true;
        Logger.d(TAG, "[download] download.");
        https = new Https().url(url);
        https.download(new File(dir, videoFilename), new Https.DownloadListener() {
            @Override
            public void onProgress(long progress, long total) {
            }

            @Override
            public void onCompleted() {
                Logger.d(TAG, "[download] download completed");
                downloading = false;
                saveLocal(url);
                https = null;

                if (!prepared) {
                    Logger.d(TAG, "[download] try prepared local again...");
                    release();
                    preparePlayer();
                }
            }

            @Override
            public void onFailure(Exception e) {
                downloading = false;
                Logger.e(TAG, "[download] an error occured when download : " + (e == null ? "null" : e.getMessage()));
            }
        });
    }

    public synchronized void addListener(FMListener listener) {
        if (listeners == null) {
            listeners = new ArrayList<>();
        }
        listeners.add(listener);
    }

    public synchronized void removeListener(FMListener listener) {
        if (listeners != null) {
            listeners.remove(listener);
        }
    }

    private synchronized void notifyStart() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onStarted();
            }
        }
    }

    private synchronized void notifyPause() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onPaused();
            }
        }
    }

    private synchronized void notifyCompleted() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onCompleted();
            }
        }
    }

    private synchronized void notifyPrepared() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onPrepared();
            }
        }
    }

    private synchronized void notifyReset() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onReset();
            }
        }
    }

    private synchronized void notifyProgress() {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onPlaying(getCurrentPosition());
            }
        }
    }

    private synchronized void notifyNewArticle(boolean isNew) {
        if (listeners != null) {
            for (FMListener listener : listeners) {
                listener.onArticleChange(isNew);
            }
        }
    }

    public void initArticleId(int articleId) {
        this.articleId = articleId;
        boolean fmShowRed = GTConfig.instance().getBooleanValue("fmShowRed", false);
        setArticleIsNew(fmShowRed);
    }

    public void setArticleId(int articleId) {
        if (articleId == -1) {
            return;
        }
        if (this.articleId != articleId) {
            setArticleIsNew(true);
        }
        this.articleId = articleId;
    }

    public int getArticleId() {
        return this.articleId;
    }

    public void setArticleIsNew(boolean isNew) {
        if (this.articleIsNew != isNew) {
            this.articleIsNew = isNew;
            notifyNewArticle(isNew);
            GTConfig.instance().setBooleanValue("fmShowRed", isNew);
        }
    }

    public boolean getArticleIsNew() {
        return this.articleIsNew;
    }

    public interface FMListener {

        void onStarted();

        void onPaused();

        void onCompleted();

        void onPrepared();

        void onReset();

        void onPlaying(int progress);

        void onArticleChange(boolean isNew);

    }

    public static class SimpleListener implements FMListener {

        @Override
        public void onStarted() {
        }

        @Override
        public void onPaused() {
        }

        public void onCompleted() {
        }

        @Override
        public void onPrepared() {
        }

        @Override
        public void onReset() {
        }

        @Override
        public void onPlaying(int progress) {
        }

        public void onArticleChange(boolean isNew) {
        }

    }

}
